/**
 * @file    startup_MK20D5.s
 * @brief
 *
 * DAPLink Interface Firmware
 * Copyright (c) 1997 - 2016, Freescale Semiconductor, Inc.
 * Copyright 2016 - 2017 NXP
 * Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*****************************************************************************/
/* Version: GCC for ARM Embedded Processors                                  */
/*****************************************************************************/
    .syntax unified                     /* ARM和THUMB指令使用统一的语法 */
    .arch armv7-m                       /* 指定架构*/

/*
a section is allocatable
d section is a GNU_MBIND section
e section is excluded from executable and shared library.
w section is writable
x section is executable
M section is mergeable
S section contains zero terminated strings
G section is a member of a section group
T section is used for thread-local-storage
.isr_vector段,以及段属性*/
    .section .isr_vector, "a"           
    .align 2                        
    .globl __isr_vector                             /* 全局标签,外部文件,在链接时可能会用到 */
__isr_vector:                                       /* 标签 */ 
    .long   __StackTop                              /* Top of Stack */
    .long   Reset_Handler                           /* Reset Handler */
    .long   NMI_Handler                             /* NMI Handler*/
    .long   HardFault_Handler                       /* Hard Fault Handler*/
    .long   MemManage_Handler                       /* MPU Fault Handler*/
    .long   BusFault_Handler                        /* Bus Fault Handler*/
    .long   UsageFault_Handler                      /* Usage Fault Handler*/
    .long   0                                       /* Reserved*/
    .long   DAPLINK_BUILD_KEY                       /* DAPLINK: Build type (BL/IF)*/
    .long   DAPLINK_HIC_ID                          /* DAPLINK: Compatibility*/
    .long   DAPLINK_VERSION                         /* DAPLINK: Version*/
    .long   SVC_Handler                             /* SVCall Handler*/
    .long   DebugMon_Handler                        /* Debug Monitor Handler*/
    .long   g_board_info                            /* DAPLINK: Pointer to board/family/target info*/
    .long   PendSV_Handler                          /* PendSV Handler*/
    .long   SysTick_Handler                         /* SysTick Handler*/

    /* External Interrupts*/                
    .long   WWDG_IRQHandler                         /* Window Watchdog */
    .long   PVD_IRQHandler                          /* PVD through EXTI Line detect */
    .long   TAMPER_IRQHandler                       /* Tamper */
    .long   RTC_IRQHandler                          /* RTC */
    .long   FLASH_IRQHandler                        /* Flash */
    .long   RCC_IRQHandler                          /* RCC */
    .long   EXTI0_IRQHandler                        /* EXTI Line 0 */
    .long   EXTI1_IRQHandler                        /* EXTI Line 1 */
    .long   EXTI2_IRQHandler                        /* EXTI Line 2 */
    .long   EXTI3_IRQHandler                        /* EXTI Line 3 */
    .long   EXTI4_IRQHandler                        /* EXTI Line 4 */
    .long   DMA1_Channel1_IRQHandler                /* DMA1 Channel 1 */
    .long   DMA1_Channel2_IRQHandler                /* DMA1 Channel 2 */
    .long   DMA1_Channel3_IRQHandler                /* DMA1 Channel 3 */
    .long   DMA1_Channel4_IRQHandler                /* DMA1 Channel 4 */
    .long   DMA1_Channel5_IRQHandler                /* DMA1 Channel 5 */
    .long   DMA1_Channel6_IRQHandler                /* DMA1 Channel 6 */
    .long   DMA1_Channel7_IRQHandler                /* DMA1 Channel 7 */
    .long   ADC1_2_IRQHandler                       /* ADC1_2 */
    .long   USB_HP_CAN1_TX_IRQHandler               /* USB High Priority or CAN1 TX */
    .long   USB_LP_CAN1_RX0_IRQHandler              /* USB Low  Priority or CAN1 RX0 */
    .long   CAN1_RX1_IRQHandler                     /* CAN1 RX1 */
    .long   CAN1_SCE_IRQHandler                     /* CAN1 SCE */
    .long   EXTI9_5_IRQHandler                      /* EXTI Line 9..5 */
    .long   TIM1_BRK_IRQHandler                     /* TIM1 Break */
    .long   TIM1_UP_IRQHandler                      /* TIM1 Update */
    .long   TIM1_TRG_COM_IRQHandler                 /* TIM1 Trigger and Commutation */
    .long   TIM1_CC_IRQHandler                      /* TIM1 Capture Compare */
    .long   TIM2_IRQHandler                         /* TIM2 */
    .long   TIM3_IRQHandler                         /* TIM3 */
    .long   TIM4_IRQHandler                         /* TIM4 */
    .long   I2C1_EV_IRQHandler                      /* I2C1 Event */
    .long   I2C1_ER_IRQHandler                      /* I2C1 Error */
    .long   I2C2_EV_IRQHandler                      /* I2C2 Event */
    .long   I2C2_ER_IRQHandler                      /* I2C2 Error */
    .long   SPI1_IRQHandler                         /* SPI1 */
    .long   SPI2_IRQHandler                         /* SPI2 */
    .long   USART1_IRQHandler                       /* USART1 */
    .long   USART2_IRQHandler                       /* USART2 */
    .long   USART3_IRQHandler                       /* USART3 */
    .long   EXTI15_10_IRQHandler                    /* EXTI Line 15..10 */
    .long   RTC_Alarm_IRQHandler                    /* RTC Alarm through EXTI Line */
    .long   USBWakeUp_IRQHandler                    /* USB Wakeup from suspend */

    .size    __isr_vector, . - __isr_vector         /* 定义__isr_vector段的大小, . 表示当前地址, __isr_vector表示段首地址,相减得到段大小*/

    .text   // 接下来是代码段
    .thumb  // 使用thumb指令集

/* 只要是从flash启动，都是从Reset_Handler中开始启动的 */

    .thumb_func                         // 用于标记接下来的函数是一个 Thumb 函数
    .align 2                            // 按照 2 字节对齐
    .globl   Reset_Handler              // 声明 Reset_Handler 这个符号是全局可见的
    .weak    Reset_Handler              // 将 Reset_Handler 定义为弱符号（weak symbol）。
    .type    Reset_Handler, %function   // 指明 Reset_Handler 的类型是函数类型
Reset_Handler:
/*cpsid 是 “Change Processor State Immediately Disable” 的缩写，后面跟着的 i 表示要禁止
（disable）中断。这条指令的作用就是在处理器层面将中断进行屏蔽，也就是让处理器暂时不响应
外部的中断请求。在复位处理的初始阶段，通常需要先禁止中断，以保证系统能按照既定的初始化流
程稳定地进行设置，避免在初始化还未完成时就因中断的触发而出现不可预期的行为。*/
    cpsid   i
/*.equ 是汇编中的伪指令，用于定义一个符号常量。在这里它定义了 VTOR 这个符号，并且将其值设定
为十六进制的 0xE000ED08。在基于 ARM 架构的嵌入式系统中，VTOR 通常是指 “向量表偏移寄存器
（Vector Table Offset Register）” 的地址，这个寄存器用于指定中断向量表在内存中的实际存
放位置（可以通过修改它的值来重定位中断向量表）。*/
    .equ    VTOR, 0xE000ED08
/*ldr 指令的一种用法是用于加载一个立即数（这里是通过 = 这种伪指令的形式）到指定寄存器中。
这条指令会将之前定义的 VTOR 所代表的地址值（即 0xE000ED08 ）加载到寄存器 r0 中，使得后
续可以通过 r0 来访问向量表偏移寄存器所在的内存地址。*/
    ldr     r0, =VTOR
    ldr     r1, =__isr_vector
/*str 是存储（store）指令，这条指令的作用是将寄存器 r1 中的值（也就是中断向量表的起始地址）
存储到以寄存器 r0 的值为地址的内存单元中（因为 r0 中存放的是 VTOR 对应的地址，也就是向量
表偏移寄存器的地址）。简单来说，就是将中断向量表的起始地址配置到向量表偏移寄存器中，从而
让处理器知道去哪里查找中断服务程序的入口地址等相关中断向量信息。*/
    str     r1, [r0]

/*从以寄存器 r1 的值为地址的内存单元中读取数据，并将读取到的值存放到寄存器 r2 中。由于 r1 
之前指向的是中断向量表起始地址，这里通常是读取中断向量表中的第一个元素（在很多 ARM 架构的
嵌入式系统中，中断向量表的第一个元素往往是主堆栈指针 MSP 的初始值）。*/
    ldr     r2, [r1]
/*msr 是 “Move to Special Register” 的缩写，用于向特殊寄存器写入数据。这里是将寄存器 r2 
中的值（也就是从中断向量表中获取到的主堆栈指针初始值）写入到 msp（主堆栈指针，Main Stack Pointer）
寄存器中，完成主堆栈指针的初始化设置，为后续函数调用、局部变量存储等涉及堆栈操作的行为准备好合
适的堆栈空间。*/
    msr     msp, r2

/*下面这一段复制于Air32f013的启动文件中，据文档说明
是通过下面对寄存器的操作，打开97k ram 和 QSPI的功能
但是查询相关寄存器文档，找不到对应寄存器的说明，所以
也无法对下面程序进行解释*/
	ldr r0,=0x400210F0
	mov r1,#1
	str r1,[r0]            // 将1写入寄存器0x400210F0
	ldr r2,=0x40016C00
	ldr r3,=0xa7d93a86    
	str r3,[r2]            // 写入立即数1到寄存器0x40016C00
	ldr r3,=0xab12dfcd
	str r3,[r2]            // 写入立即数2到寄存器0x40016C00
	ldr r3,=0xcded3526
	str r3,[r2]            // 写入立即数3到寄存器0x40016C00
	ldr r3,=0x200183FF
	str r3,[r2,#0x18] 	   // 写入立即数4到寄存器0x40016C00 + 0x18的位置 让内存容量从32k 变为 97k(!)
	ldr r4,=0x4002228c
	ldr r5,=0xa5a5a5a5     // 写入立即数6到寄存器0x4002228C
	str r5,[r4] 		   // 注释说是开户QSPI功能
#ifndef __NO_SYSTEM_INIT
/*
ldr r0,=SystemInit 指令会加载 SystemInit 函数的地址到寄存器 r0 中，这里假设 SystemInit 
是一个用于进行系统级初始化的函数（比如初始化时钟、外设等相关硬件资源配置等工作）。
*/
    ldr   r0,=SystemInit
/*
blx r0 指令是分支并链接（Branch with Link and Exchange）指令，它会跳转到寄存器 r0 
所指向的地址（也就是 SystemInit 函数的入口地址）去执行 SystemInit 函数，并且在跳转前
会将当前的返回地址等相关信息保存到链接寄存器中，以便 SystemInit 函数执行完后能正确返
回到 Reset_Handler 中继续执行后续代码。
*/
    blx   r0
#endif
/*
这段代码主要实现的是对数据段（.data 段）的初始化操作，通常用于将存储在只读存储器
（比如 Flash）中的初始数据值复制到可读写的随机存取存储器（比如 RAM）中的对应数据区域，
确保程序运行时数据段中的变量能获取到正确的初始值，常见于嵌入式系统的启动代码中。
   __etext: 代码段结束地址,数据段开始地址.
   __data_start__/__data_end__: 内存地址,必须四字节对齐
*/

    ldr    r1, =__etext
    ldr    r2, =__data_start__
    ldr    r3, =__data_end__

/* 第一种循环实现方式：其优势在于对代码尺寸方面比较有利，也就是这种实现方式生成的
机器代码占用的存储空间相对较小。在一些资源受限的嵌入式系统中，代码尺寸是需要重点
考量的因素，例如在存储容量有限的微控制器上运行程序时，较小的代码尺寸意味着能有更
多的空间留给其他功能模块或者数据存储等用途，所以在默认情况下会使用这种方式。
第二种循环实现方式：它的特点是更侧重于性能方面的优化，很可能在执行速度上会比第一
种方式更快，例如采用了更高效的指令组合、更少的指令周期来完成同样的循环逻辑等。
不过，这种性能优势往往可能是以牺牲一定的代码空间为代价换来的，也就是其生成的机器代
码尺寸可能相对较大。*/
#if 1
.LC0:
/*
cmp 是比较（compare）指令，它会比较寄存器 r2（数据段起始地址）和寄存器 r3
（数据段结束地址）中的值。比较的结果会影响后续的条件执行指令，比如根据比较结
果判断是否执行后面的加载 - 存储操作等，其目的是确定当前是否已经处理完整个数据
段的数据复制操作。
*/
    cmp     r2, r3
/*
这是一条条件执行指令，ittt 表示如果前面的比较结果是小于（lt ，less than），
那么接下来的三条指令（以 lt 为条件限定）将会被执行。它基于比较结果来决定后续
指令是否生效，从而实现有条件的代码执行逻辑，在这里主要是用于针对数据段还未处
理完的情况进行相应操作。
*/
    ittt    lt
/*
ldrlt 表示如果满足前面的小于条件（lt ），就执行加载（ldr ）操作。这条指令会
从以寄存器 r1 的值为地址的内存单元中读取一个 32 位（因为在 ARM 架构下一般默
认是 32 位数据加载，当然具体也和指令设置等有关）的数据，并将其存放到寄存器 r0 
中，然后寄存器 r1 的值会自动增加 4（通过 #4 实现地址递增，因为每个数据单元按 
4 字节对齐等情况居多，这里以 4 字节为步长进行地址推进，准备读取下一个数据）。
本质上就是从代码段末尾附近（r1 起始指向 __etext ）按顺序读取数据段的初始数据值。
*/
    ldrlt   r0, [r1], #4
    strlt   r0, [r2], #4
/*
blt 表示如果比较结果是小于（lt ），就跳转到标签 .LC0 处继续执行代码，从而形成
一个循环结构，不断重复上述的数据读取和存储操作，直到数据段中的所有数据都被正确
复制完成（即 r2 达到或超过 r3 所代表的数据段结束地址）。
*/
    blt    .LC0
#else
/*
subs 是带借位减法（Subtract with Carry）指令，它会用寄存器 r3（数据段结束地址）
中的值减去寄存器 r2（数据段起始地址）中的值，并根据结果更新标志位（比如借位标志、
零标志等），同时将差值存回寄存器 r3 中，这样 r3 中就存放了数据段中还剩余需要处理
的数据元素个数（以字节数等为衡量，这里实际上按 4 字节为单位来处理更合适，后面操作
体现这点）。
*/
    subs    r3, r2
/*
ble 表示如果前面减法操作的结果是小于等于（less than or equal ），就跳转到标签 .LC1 
处，意味着如果数据段中已经没有或者只剩下很少的数据需要处理（比如已经处理完或者只剩最
后一个数据元素等情况），就跳过下面的循环处理部分，直接进入到 .LC1 对应的后续代码逻辑中
（可能是结束数据段初始化等相关收尾操作）。
*/
    ble    .LC1
.LC0:
/*
subs 指令在这里先将寄存器 r3 的值减去 4，其目的是按 4 字节为单位来逐步递减剩余需要
处理的数据元素个数（因为数据在内存中一般按 4 字节对齐存储且每次操作处理一个 4 字节的
数据单元，所以以 4 字节为步长来推进处理过程）。
*/
    subs    r3, #4
/*
从以寄存器 r1（初始指向 __etext ，代码段结束地址附近）的值加上寄存器 r3 当前的值
（偏移量，代表当前要处理的数据在代码段中的相对位置）为地址的内存单元中读取一个 32 
位数据存放到寄存器 r0 中，也就是按剩余数据元素个数对应的偏移量从代码段读取相应的数据。
*/
    ldr    r0, [r1, r3]
/*
把寄存器 r0 中的数据存储到以寄存器 r2（数据段起始地址）的值加上寄存器 r3 当前的值
（同样的偏移量）为地址的内存单元中，实现将从代码段读取的数据按顺序写入到数据段对应位置
的内存单元中，完成数据复制操作。
*/
    str    r0, [r2, r3]
/*
bgt 表示如果前面减法操作等导致的结果是大于（greater than ），就跳转到标签 .LC0 处
继续循环执行上述操作，持续处理数据段中的剩余数据，直到所有数据都被复制完成（即 r3 减
到小于等于 0 等情况）。
*/
    bgt    .LC0
.LC1:
#endif

#ifdef __STARTUP_CLEAR_BSS
/*
这部分工作通常在 C 语言库的启动代码中完成。否则，
定义此宏以便在这个启动代码中启用它。
循环将 BSS 段清零，该操作会用到链接脚本中的以下符号：
bss_start：BSS 段的起始位置。必须按 4 字节对齐
bss_end：BSS 段的结束位置。必须按 4 字节对齐 */
*/
    ldr r1, =__bss_start__
    ldr r2, =__bss_end__
/*
将立即数 0 移动（赋值）到寄存器 r0 中，目的是准备用这个 0 值去初始化 BSS 段中的每个存储单元。
*/
    movs    r0, 0
.LC2:
/*
比较寄存器 r1（存放 BSS 段起始地址）和寄存器 r2（存放 BSS 段结束地址）中的值，
通过比较来判断是否已经处理完整个 BSS 段。
*/
    cmp     r1, r2
/*
这是一条条件执行指令，“itt”表示如果前面的比较结果是小于（lt，less than），
那么接下来的一条指令（以“lt”为条件限定）将会被执行，基于比较结果决定后续指
令是否生效，实现有条件的代码执行逻辑。
*/
    itt    lt
/*
“strlt”表示如果满足前面的小于条件（lt），就执行存储（str）操作。这条指令会
把寄存器 r0 中的值（也就是前面赋的 0 值）存储到以寄存器 r1 的值为地址的内存单元中，
然后寄存器 r1 的值会自动增加 4（通过“#4”实现地址递增，因为通常数据按 4 字节对齐
等情况居多，这里以 4 字节为步长进行地址推进，准备处理下一个存储单元），本质上就是将 BSS 段中的存储单元逐个初始化为 0。
*/
    strlt   r0, [r1], #4
    blt    .LC2
#endif
/*
cpsie 是 “Change Processor State Immediately Enable” 的缩写，后面跟着的 i 表示
要使能（enable）中断。与之前提到的 cpsid i（禁止中断）相对应，这条指令的作用就是在
处理器层面将中断放开，允许处理器响应外部的中断请求。通常在系统完成了关键的初始化步骤（
比如前面进行的中断向量表配置、数据段初始化、BSS 段清零等操作）后，就可以开启中断，
以便系统能正常处理各种外部事件触发的中断情况了。
*/
    cpsie   i
/*
#ifndef 是条件编译指令，用于判断是否没有定义某个宏。这里它检查是否没有定义 __START 宏，
如果没有定义，就通过 #define 指令定义 __START 宏，并将其值设为 _start。这样做的目的可
能是为了统一程序启动入口的标识，确保后续代码在引用启动入口时使用一个规范的符号，方便代码
的移植、维护以及与不同编译环境等的适配。
*/
#ifndef __START
#define __START _start
#endif

#ifndef __ATOLLIC__
/*
如果没有定义 __ATOLLIC__ 宏，那么会执行这部分代码。ldr r0,=__START 指令会加载 __START
（前面可能已经定义为 _start ）这个符号所代表的地址到寄存器 r0 中，而 blx r0 指令是分支
并链接（Branch with Link and Exchange）指令，它会跳转到寄存器 r0 所指向的地址（也就是 
_start 对应的地址）去执行相应的代码逻辑，这里 _start 通常是整个程序真正开始执行的起始点
，类似主函数的入口作用，后续的程序流程会从这个点正式展开。
这个__start应该是库函数，没有源码
    ldr   r0,=_start
    blx   r0
*/
    bl _start
#else
/*
当定义了 __ATOLLIC__ 宏时，会执行这一系列指令。首先，ldr r0,=__libc_init_array 指令将
 __libc_init_array 符号对应的地址加载到寄存器 r0 中，然后通过 blx r0 指令跳转到该地址
 去执行 __libc_init_array 相关的函数逻辑，这个函数一般是用于初始化 C 语言标准库相关的
 数组等数据结构或执行一些库的初始化操作。接着，ldr r0,=main 指令会加载 main 函数（也就
 是 C 语言程序中大家熟知的主函数）的地址到寄存器 r0 中，最后通过 bx r0 指令跳转到 main 
 函数去执行，标志着程序正式进入用户编写的主函数逻辑部分，开始执行具体的业务功能代码。
 这个__libc_init_array应该是库函数，没有源码
*/
    ldr   r0,=__libc_init_array
    blx   r0
    ldr   r0,=main
    bx    r0
#endif
/*
.pool 是汇编语言中的伪指令，它的作用是提示汇编器将前面出现的文字池（literal pool）
中的数据进行合适的处理和放置。文字池通常用于存放一些立即数或者常量数据，汇编器在生
成目标代码时，需要合理安排这些数据在内存中的位置，.pool 指令就是告诉汇编器可以对这
些数据进行相关的内存布局操作了，确保它们能被正确地访问和使用。
*/
    .pool
/*
这条指令用于计算 Reset_Handler 这个函数所占据的内存空间大小。它通过计算当前地址
（ . 表示当前地址）与 Reset_Handler 函数起始地址的差值来得到函数的大小，与前面提
到的计算中断向量表大小等类似，汇编器、链接器等工具可以依据这个信息来准确地对函数代
码在内存中进行布局，比如确定函数代码存放在哪个具体的内存区间，以及判断是否符合内
存空间规划等要求。
*/
    .size Reset_Handler, . - Reset_Handler

/*
要求进行内存对齐操作，这里是按照 1 字节对齐。内存对齐有助于提高处理器访问内存的效
率以及保证数据存储和读取的规范性等。虽然按照 1 字节对齐相对来说对齐要求不是很高（
相比于比如按 4 字节对齐等情况），但在一些特定的内存布局需求或者为了遵循特定的代码
规范、与其他模块的内存衔接等场景下会有应用，确保后续定义的函数等代码在内存中的位
置符合特定的对齐规则。
*/
    .align  1
    .thumb_func
    .weak DefaultISR
    .type DefaultISR, %function
/*
DefaultISR 函数内只有一条指令 b DefaultISR，这是一条无条件跳转指令，它会跳转到
函数自身（也就是 DefaultISR ），形成了一个死循环。这样的设计通常意味着这是一个
默认的、最简的中断处理程序形式，当没有用户自定义的更具体的中断处理逻辑时，进入
到这个默认中断处理程序就会一直在这个循环里等待，相当于暂时不做实际的有效处理动作
（当然实际应用中往往需要用户根据具体中断需求去完善其功能）。
*/
DefaultISR:
    b DefaultISR
    .size DefaultISR, . - DefaultISR

    .align 1
    .thumb_func
    .weak NMI_Handler
    .type NMI_Handler, %function
/*
NMI_Handler 函数内有两条指令，首先 ldr r0,=NMI_Handler 会加载 NMI_Handler 函数自
身的地址到寄存器 r0 中，然后 bx r0 指令是分支并交换（Branch and Exchange）指令，
它会跳转到寄存器 r0 所指向的地址，也就是又跳回了 NMI_Handler 函数自身，同样形成了一
个循环结构。不过与 DefaultISR 的简单跳转不同，这里先加载自身地址再跳转的方式可能在某
些特定的处理器机制或者调试相关场景下有一定作用，比如可以方便地通过查看寄存器 r0 的值
来确认当前所处的中断处理函数入口地址等情况，但本质上也是一种默认的、简易的中断处理逻
辑，通常等待用户进一步完善功能。
*/
NMI_Handler:
    ldr   r0,=NMI_Handler
    bx    r0
    .size NMI_Handler, . - NMI_Handler

    .align 1
    .thumb_func
    .weak HardFault_Handler
    .type HardFault_Handler, %function
HardFault_Handler:
    ldr   r0,=HardFault_Handler
    bx    r0
    .size HardFault_Handler, . - HardFault_Handler

    .align 1
    .thumb_func
    .weak MemManage_Handler
    .type MemManage_Handler, %function
MemManage_Handler:
    ldr   r0,=MemManage_Handler
    bx    r0
    .size MemManage_Handler, . - MemManage_Handler

    .align 1
    .thumb_func
    .weak BusFault_Handler
    .type BusFault_Handler, %function
BusFault_Handler:
    ldr   r0,=BusFault_Handler
    bx    r0
    .size BusFault_Handler, . - BusFault_Handler

    .align 1
    .thumb_func
    .weak UsageFault_Handler
    .type UsageFault_Handler, %function
UsageFault_Handler:
    ldr   r0,=UsageFault_Handler
    bx    r0
    .size UsageFault_Handler, . - UsageFault_Handler

    .align 1
    .thumb_func
    .weak SVC_Handler
    .type SVC_Handler, %function
SVC_Handler:
    ldr   r0,=SVC_Handler
    bx    r0
    .size SVC_Handler, . - SVC_Handler

    .align 1
    .thumb_func
    .weak DebugMon_Handler
    .type DebugMon_Handler, %function
DebugMon_Handler:
    ldr   r0,=DebugMon_Handler
    bx    r0
    .size DebugMon_Handler, . - DebugMon_Handler

    .align 1
    .thumb_func
    .weak PendSV_Handler
    .type PendSV_Handler, %function
PendSV_Handler:
    ldr   r0,=PendSV_Handler
    bx    r0
    .size PendSV_Handler, . - PendSV_Handler

    .align 1
    .thumb_func
    .weak SysTick_Handler
    .type SysTick_Handler, %function
SysTick_Handler:
    ldr   r0,=SysTick_Handler
    bx    r0
    .size SysTick_Handler, . - SysTick_Handler

/*
这段代码定义了一个名为 def_irq_handler 的宏，其主要作用是用于辅助定义中断处理程序
（Interrupt Service Routines，ISRs）相关的函数符号，通过设置弱符号以及将函数符号
默认关联到一个默认的中断处理程序（这里是 DefaultISR ），来方便在整个程序的编译、
链接以及中断处理逻辑构建过程中进行相关操作，提供一种统一的、便于管理和扩展的方式
来处理中断处理程序相关的定义。
*/
/*
这是宏定义的起始部分，def_irq_handler 是宏的名称，而 handler_name 是宏的参数。
意味着在后续使用这个宏的时候，需要传入一个具体的函数名称（也就是具体的中断处理程
序名称）作为参数，宏会根据传入的这个参数进行一系列的操作来对相应的中断处理程序进
行设置。
*/
    .macro def_irq_handler  handler_name
/*
\handler_name 这里表示对传入的宏参数（也就是具体的中断处理程序名称）进行引用。
这条指令的作用是将传入的这个函数名称对应的符号定义为弱符号（weak symbol）。
弱符号的特性如前面所述，在链接过程中，如果存在其他同名的强符号，那么优先使用强
符号对应的定义；若没有同名强符号，则使用这个弱符号定义的函数。通过将中断处理程序
名称定义为弱符号，为后续用户可能进行的自定义中断处理程序替换默认实现等操作提供
了灵活性，方便根据实际需求调整具体的中断处理逻辑。

*/
    .weak \handler_name
/*
同样，\handler_name 引用传入的具体中断处理程序名称，这条指令使用 .set 伪指令
将该函数名称对应的符号设置为 DefaultISR 。这意味着在默认情况下，该中断处理程序
（通过传入的函数名称来标识）会被关联到 DefaultISR 这个默认的中断处理程序上。
也就是说，如果用户没有另行自定义该中断处理程序的具体实现，那么在程序运行时遇到对
应的中断触发，就会执行 DefaultISR 所代表的默认中断处理逻辑（通常是一个简单的循
环等默认动作，等待用户完善具体处理内容）。
*/
    .set  \handler_name, DefaultISR
    .endm

/* Exception Handlers */
    def_irq_handler WWDG_IRQHandler            /* Window Watchdog */
    def_irq_handler PVD_IRQHandler             /* PVD through EXTI Line detect */
    def_irq_handler TAMPER_IRQHandler          /* Tamper */
    def_irq_handler RTC_IRQHandler             /* RTC */
    def_irq_handler FLASH_IRQHandler           /* Flash */
    def_irq_handler RCC_IRQHandler             /* RCC */
    def_irq_handler EXTI0_IRQHandler           /* EXTI Line 0 */
    def_irq_handler EXTI1_IRQHandler           /* EXTI Line 1 */
    def_irq_handler EXTI2_IRQHandler           /* EXTI Line 2 */
    def_irq_handler EXTI3_IRQHandler           /* EXTI Line 3 */
    def_irq_handler EXTI4_IRQHandler           /* EXTI Line 4 */
    def_irq_handler DMA1_Channel1_IRQHandler   /* DMA1 Channel 1 */
    def_irq_handler DMA1_Channel2_IRQHandler   /* DMA1 Channel 2 */
    def_irq_handler DMA1_Channel3_IRQHandler   /* DMA1 Channel 3 */
    def_irq_handler DMA1_Channel4_IRQHandler   /* DMA1 Channel 4 */
    def_irq_handler DMA1_Channel5_IRQHandler   /* DMA1 Channel 5 */
    def_irq_handler DMA1_Channel6_IRQHandler   /* DMA1 Channel 6 */
    def_irq_handler DMA1_Channel7_IRQHandler   /* DMA1 Channel 7 */
    def_irq_handler ADC1_2_IRQHandler          /* ADC1_2 */
    def_irq_handler USB_HP_CAN1_TX_IRQHandler  /* USB High Priority or CAN1 TX */
    def_irq_handler USB_LP_CAN1_RX0_IRQHandler /* USB Low  Priority or CAN1 RX0 */
    def_irq_handler CAN1_RX1_IRQHandler        /* CAN1 RX1 */
    def_irq_handler CAN1_SCE_IRQHandler        /* CAN1 SCE */
    def_irq_handler EXTI9_5_IRQHandler         /* EXTI Line 9..5 */
    def_irq_handler TIM1_BRK_IRQHandler        /* TIM1 Break */
    def_irq_handler TIM1_UP_IRQHandler         /* TIM1 Update */
    def_irq_handler TIM1_TRG_COM_IRQHandler    /* TIM1 Trigger and Commutation */
    def_irq_handler TIM1_CC_IRQHandler         /* TIM1 Capture Compare */
    def_irq_handler TIM2_IRQHandler            /* TIM2 */
    def_irq_handler TIM3_IRQHandler            /* TIM3 */
    def_irq_handler TIM4_IRQHandler            /* TIM4 */
    def_irq_handler I2C1_EV_IRQHandler         /* I2C1 Event */
    def_irq_handler I2C1_ER_IRQHandler         /* I2C1 Error */
    def_irq_handler I2C2_EV_IRQHandler         /* I2C2 Event */
    def_irq_handler I2C2_ER_IRQHandler         /* I2C2 Error */
    def_irq_handler SPI1_IRQHandler            /* SPI1 */
    def_irq_handler SPI2_IRQHandler            /* SPI2 */
    def_irq_handler USART1_IRQHandler          /* USART1 */
    def_irq_handler USART2_IRQHandler          /* USART2 */
    def_irq_handler USART3_IRQHandler          /* USART3 */
    def_irq_handler EXTI15_10_IRQHandler       /* EXTI Line 15..10 */
    def_irq_handler RTC_Alarm_IRQHandler        /* RTC Alarm through EXTI Line */
    def_irq_handler USBWakeUp_IRQHandler       /* USB Wakeup from suspend */

    .end
